package main

import (
	"context"
	"encoding/json"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/opentracing/opentracing-go"

	"github.com/uber/jaeger-client-go"

	"github.com/prometheus/client_golang/prometheus/promhttp"

	"github.com/prometheus/client_golang/prometheus"
	jaegerCfg "github.com/uber/jaeger-client-go/config"
)

var (
	host           = "0.0.0.0"
	routerMap      = map[string]func(ctx context.Context, responseWriter http.ResponseWriter, request *http.Request){}
	latencyMetrics = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Namespace: "nacloud_httpserver_monitor",
			Subsystem: "duration",
			Name:      "latency",
			Help:      "the latency of http request",
		},
	)
	tracer opentracing.Tracer
)

type CloudServer struct{}

func (cs *CloudServer) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
	url := request.URL
	if url == nil {
		responseWriter.WriteHeader(http.StatusBadRequest)
		msg := "invalid url"
		msgByte, _ := json.Marshal(msg)
		responseWriter.Write(msgByte)
		return
	}
	// 随机休眠
	var random = rand.Intn(2000)
	time.Sleep(time.Duration(random) * time.Millisecond)
	// 记录metrics
	latencyMetrics.Observe(float64(random))

	path := url.Path
	method, ok := routerMap[path]
	// 创建root span
	sp := tracer.StartSpan("start-process-service")
	// 执行完结束这个span
	defer sp.Finish()
	// 将span传递给method
	ctx := opentracing.ContextWithSpan(context.Background(), sp)
	if ok {
		method(ctx, responseWriter, request)
		return
	}
	cs.notFound(ctx, responseWriter)
}

func (cs *CloudServer) notFound(ctx context.Context, responseWriter http.ResponseWriter) {
	span, ctx := opentracing.StartSpanFromContext(ctx, "NotFound")
	defer span.Finish()
	responseWriter.WriteHeader(http.StatusNotFound)
	responseWriter.Write([]byte("404 Not Found"))
}

// register router
func (cs *CloudServer) RegisterRouter(path string, handleMethod func(ctx context.Context, responseWriter http.ResponseWriter, request *http.Request)) {
	if path == "" {
		return
	}
	routerMap[path] = handleMethod
}

func (cs *CloudServer) HelloMethod(ctx context.Context, writer http.ResponseWriter, request *http.Request) {
	span, ctx := opentracing.StartSpanFromContext(ctx, "HelloMethod")
	defer span.Finish()
	response := "Hello, healthz"
	responseByte, _ := json.Marshal(response)
	requestHeader := request.Header
	for k, v := range requestHeader {
		if len(v) == 0 {
			continue
		}
		writer.Header().Set(k, v[0])
	}
	version := os.Getenv("VERSION")
	writer.Header().Set("VERSION", version)
	writer.Header().Set("client_addr", request.RemoteAddr)
	writer.WriteHeader(http.StatusOK)
	log.Printf("client_address: %s, http status code: %d", request.RemoteAddr, http.StatusOK)
	writer.Write(responseByte)
}
func (cs *CloudServer) Server(port int) {
	if port < 1000 {
		errMsg := "Server port should greater than 1000"
		log.Fatalf(errMsg)
		panic(errMsg)
	}
	portStr := strconv.Itoa(port)
	serverAddr := host + ":" + portStr
	cs.RegisterRouter("/healthz", cs.HelloMethod)
	http.Handle("/metrics", promhttp.Handler())
	http.ListenAndServe(serverAddr, cs)
}

func main() {
	cs := &CloudServer{}
	closer := initJaeger("na-cloud-http-server")
	defer closer.Close()
	tracer = opentracing.GlobalTracer()
	prometheus.MustRegister(latencyMetrics)
	cs.Server(6789)
}

func initJaeger(svc string) io.Closer {
	config := jaegerCfg.Configuration{
		Sampler: &jaegerCfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1,
		},
		Reporter: &jaegerCfg.ReporterConfig{
			LogSpans:          true,
			CollectorEndpoint: "http://localhost:14268/api/traces", //上报地址
		},
	}
	closer, err := config.InitGlobalTracer(svc, jaegerCfg.Logger(jaeger.StdLogger))
	if err != nil {
		panic("cannot init jaeger, " + err.Error())
	}
	return closer
}
